package vsa.migrations.junit.config;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
import java.util.logging.Logger;

import org.junit.Assert;

import vsa.migrations.junit.migrators.AbstractJunitMigrator;
import vsa.migrations.junit.migrators.JunitMigrator;

public class JunitMigrationConfigurator {

	private static Logger LOGGER = Logger.getLogger(JunitMigrationConfigurator.class.getName());

	private String usedAssertClass = Assert.class.getName();
	
	private List<String> excludePaths = new ArrayList<String>();

	private final String PROPFILE_NAME = "junitmigration.properties";

	private Properties props;

	/**
	 * We have to map abstract test-base-classes to new testrunners
	 */
	private final HashMap<String, String> runnerMapping = new HashMap<String, String>();

	private List<JunitMigrator> migrators = new ArrayList<JunitMigrator>();

	/**
	 * Constructor, reads the propertyfile junitmigration.properties, if available
	 */
	public JunitMigrationConfigurator () {
		if (props == null) {
		  props = new Properties();
		  try {

			URL resource = getClass().getResource("/" + PROPFILE_NAME);
			if (resource == null)
				throw new IllegalStateException("No propertyfile " + PROPFILE_NAME + " found");

			File propFileLocal = new File (PROPFILE_NAME);
			if (propFileLocal.exists()) {
				LOGGER.info("Using propertyfile " + propFileLocal.getAbsolutePath());
				props.load(new FileInputStream(propFileLocal));
			}
			else {
		      LOGGER.info("Using propertyfile " + resource.toString());
			  props.load(resource.openStream());
			}
			
			String excludePathString = (String) props.get("excludePaths");
			if (excludePathString != null && ! excludePathString.isEmpty()) {
				excludePaths.addAll(Arrays.asList(excludePathString.split(",")));
				LOGGER.info("Using excludePaths " + excludePaths);
			}
			
			usedAssertClass = (String) props.get("assertClass");
			for (Object nextKey : props.keySet()) {
				if (nextKey instanceof String) {
					String nextKeyAsString = (String) nextKey;

					if (nextKeyAsString.startsWith("runwithMapping")) {
						String nextValueAsString = (String) props.get(nextKeyAsString);
						String [] mapping = nextValueAsString.split("->");
						if (mapping.length != 2)
							throw new RuntimeException("Mapping-configuration " + nextValueAsString +
									" in propertyfile " + resource.toString() + " was not correct, should be 'TESTCLASS->RUNNER'");
						getRunnerMapping().put(mapping [0], mapping [1]);
					}
				}
			}

			String migratorsValue = (String) props.get("migrators");
			if (migratorsValue == null)
				throw new IllegalStateException("Please configure property migrators (class which implements JUnitMigrator) in propertyfile " + resource.toString());
			String [] migratorsList = migratorsValue.split(",");
			for (String nextMig: migratorsList) {
				String completeName = JunitMigrator.class.getPackage().getName() + "." + nextMig;
				Class forName = Class.forName(completeName);
				Object loadedObject = forName.newInstance();

				if (! (loadedObject instanceof AbstractJunitMigrator))
					throw new IllegalStateException("Configured migrator " + completeName + " is not an instance of AbstractJunitMigrator, " + resource.toString());

				AbstractJunitMigrator newMigrator = (AbstractJunitMigrator) loadedObject;
				newMigrator.setConfigurator(this);
				migrators.add(newMigrator);
			}


		} catch (FileNotFoundException e) {
			LOGGER.warning("File " + PROPFILE_NAME + " not found, use defaults");
		} catch (IOException e) {
			throw new RuntimeException(e);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		} catch (InstantiationException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
		}
	}


	/**
	 * getter
	 * @return used assertClass
	 */
	public String getUsedAssertClass () {
		return usedAssertClass;
	}

	/**
	 * getter
	 * @return list of packages to ignore
	 */
	public List<String> getExcludePaths () {
		return excludePaths;
	}



	/**
	 * getter
	 * @return get migrators
	 */
	public List<JunitMigrator> getMigrators() {
		return migrators;
	}


	/**
	 * getter
	 * @return mapping from abstract testcase to runner
	 */
	public HashMap<String, String> getRunnerMapping() {
		return runnerMapping;
	}




}
